<?PHP
////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2012 Alexandre Vaillancourt                                  //
//                                                                            //
// Permission is hereby granted, free of charge, to any person obtaining a    //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation  //
// the rights to use, copy, modify, merge, publish, distribute, sublicense,   //
// and/or sell copies of the Software, and to permit persons to whom the      //
// Software is furnished to do so, subject to the following conditions:       //
//                                                                            //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software.                        //
//                                                                            //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        //
// DEALINGS IN THE SOFTWARE.                                                  //
////////////////////////////////////////////////////////////////////////////////

/**
 * Class used to add functions to wikipediaapi
 * 
 * @author Alexandre Vaillancourt <alexandre.vaillancourt@gmail.com>
 * @license http://code.google.com/p/rappelz-hunbot/wiki/License MIT License
 * @copyright Copyright (c) 2012 Alexandre Vaillancourt
 * @since 
 */
 
 /**
  * Configuration for the bot
  */ 
require_once('config.php');
 
/**
 * Objects needed to acces database
 */ 
require_once('include.db_objects.stats_bot.php');

/**
 * API to access wiki
 */ 
require_once('class.wikipediaapi_improved.php');

/**
 * Eases error logging
 */
require_once('class.Log.php');


class wiki_views_stat_bot {
	private $_api_object;
	
	function __construct() {
		$this->_api_object = new wikipediaapi_improved(WIKI_URL, WIKI_USER_HAS_BOT_FLAG);
		$this->_api_object->login(WIKI_USERNAME, WIKI_USERPASS);
	}
 
	function run() {
		// get data from wiki
		 $this->fetch_namespaces();
		 $pages = $this->fetch_pages();
		 $timestamp_date = $this->fetch_views($pages);
		
		// build information with data
		 $this->compute_statistics($pages, $timestamp_date);
		
		// build stat pages
		$this->publish_statistics();
	}
	
	function fetch_namespaces() {
		
		// get namespaces from the wiki
		$namespaces_on_wiki = $this->_api_object->get_namespaces();
		ksort($namespaces_on_wiki, SORT_NUMERIC);
		
		// get what we have in DB
		$namespaces_in_db = new wiki_namespace();
		$namespaces_in_db = $namespaces_in_db->GetList(array(), 'wiki_id');
		
		// make sure all namespaces in DB are up to date
		// remembers all the namespaces that are not there yet
		foreach($namespaces_in_db as $one_namespace_in_db) {
			
			if(isset($namespaces_on_wiki[$one_namespace_in_db->wiki_id])) {
				if(strcmp($one_namespace_in_db->name, $namespaces_on_wiki[$one_namespace_in_db->wiki_id]['name']) != 0) {
					$one_namespace_in_db->name = $namespaces_on_wiki[$one_namespace_in_db->wiki_id]['name'];
					$one_namespace_in_db->Save();
				}
				
				unset($namespaces_on_wiki[$one_namespace_in_db->wiki_id]);
			}
			else {
				$one_namespace_in_db->deleted = 1;
				$one_namespace_in_db->Save();
			}
		}

		// what's left in $namespaces_on_wiki is stuff to add
		foreach($namespaces_on_wiki as $key => $value) {
			$new_namespace = new wiki_namespace();
			$new_namespace->wiki_id = $key;
			$new_namespace->name = $value['name'];
			$new_namespace->deleted = 0;
			$new_namespace->Save();
		}
		
		return 0;
	}
	
	function fetch_pages() {
	
		// Get a timestamp
		$update_timestamp = time();
		
		// get the namespaces that we have in DB
		$wiki_namespaces_in_db = new wiki_namespace();
		$wiki_namespaces_in_db = $wiki_namespaces_in_db->GetList(array(), 'wiki_id');
		$new_wiki_namespaces_in_db = array();
		foreach($wiki_namespaces_in_db as $one_wiki_namespace_in_db) {
			$new_namespaces_in_db[$one_wiki_namespace_in_db->wiki_id] = $one_wiki_namespace_in_db;
		}
		$wiki_namespaces_in_db = $new_namespaces_in_db;
		unset($new_namespaces_in_db);
	
		// mark all as deleted
		//$one_wiki_page_in_db = new wiki_page_improved();
		//$one_wiki_page_in_db->MarkAllAsDeleted();
		//$one_wiki_page_in_db = null;
		
		// get all pages in DB
		$wiki_pages_in_db = new wiki_page();
		$wiki_pages_in_db = $wiki_pages_in_db->GetList(array(array('deleted','=', '0')), 'wiki_id');
		$new_wiki_pages_in_db = array();
		foreach($wiki_pages_in_db as $one_wiki_page_in_db) {
			$new_wiki_pages_in_db[$one_wiki_page_in_db->wiki_id] = $one_wiki_page_in_db;
		}
		$wiki_pages_in_db = $new_wiki_pages_in_db;
		unset($new_wiki_pages_in_db);
		
		// update all the pages in the db
		foreach($wiki_namespaces_in_db as $one_wiki_namespace_in_db) {
			
			$wiki_pages_in_namespace = $this->_api_object->get_page_list_in_namespace($one_wiki_namespace_in_db->wiki_id);
			
			foreach($wiki_pages_in_namespace as $one_page_in_namespace) {
				$should_save = false;
				if(isset($wiki_pages_in_db[$one_page_in_namespace['pageid']])) {
					$wiki_page_object = $wiki_pages_in_db[$one_page_in_namespace['pageid']];
					// page exists, update if needed
					if(strcmp($wiki_page_object->title, $one_page_in_namespace['title']) != 0) {
						$wiki_page_object->title = $one_page_in_namespace['title'];
						$should_save = true;
					}
					if($wiki_page_object->ns != $one_wiki_namespace_in_db->wiki_id) {
						$wiki_page_object->wiki_namespaceId = $one_wiki_namespace_in_db->wiki_namespaceId;
						$should_save = true;
					}
				} 
				else {
					// new page
					$wiki_page_object = new wiki_page();
					$wiki_page_object->wiki_id = $one_page_in_namespace['pageid'];
					$wiki_page_object->title = $one_page_in_namespace['title'];
					$wiki_page_object->wiki_namespaceId = $one_wiki_namespace_in_db->wiki_namespaceId;
					$wiki_page_object->update_timestamp = 0;
					$should_save = true;
					$wiki_pages_in_db[$one_page_in_namespace['pageid']] = $wiki_page_object;
				}
				
				if($should_save === true) {
					$wiki_page_object->Save();
				}
				
				$wiki_pages_in_db[$one_page_in_namespace['pageid']]->update_timestamp = $update_timestamp;
			}
		}
		
		return $wiki_pages_in_db;
	}
	
	function fetch_views(&$pages) {
	
		// build page ids list
		$clean_page_ids = array();
	
		$dirty_page_ids = array_keys($pages);
	
		foreach($dirty_page_ids as $one_key) {
			if($pages[$one_key]->update_timestamp == 0) {
				$pages[$one_key]->deleted = 1;
				$pages[$one_key]->Save();
				unset($pages[$one_key]);
			}
			else {
				$clean_page_ids[] = $one_key;
			}
		}
		
		$visit_timestamp = time();
		
		// get views
		$page_views = $this->_api_object->get_page_views($clean_page_ids);
		
		// Compute tick
		$date_time_data = getdate($visit_timestamp);
		$current_hour_ts = $visit_timestamp - $date_time_data['seconds'] - $date_time_data['minutes'] * 60;
		$current_day_ts = 0;
		$current_week_ts = 0;
		$current_month_ts = 0;
		if($date_time_data['hours'] == STATS_DAY_START) {
			$current_day_ts = $current_hour_ts - STATS_DAY_START * 60 * 60;
			
			if($date_time_data['wday'] == STATS_WEEKDAY_START) {
				$current_week_ts = $current_day_ts - STATS_WEEKDAY_START * 24 * 60 * 60;
			}
			
			if($date_time_data['mday'] == STATS_MONTH_START) {
				$current_month_ts = $current_day_ts - STATS_MONTH_START * 24 * 60 * 60;
			}
		}
		
		// create tick object
		$db_visit_tick = new visit_tick();
		$db_visit_tick->true_timestamp = $visit_timestamp;
		$db_visit_tick->hour_timestamp = $current_hour_ts;
		$db_visit_tick->day_timestamp = $current_day_ts;
		$db_visit_tick->week_timestamp = $current_week_ts;
		$db_visit_tick->month_timestamp = $current_month_ts;
		$db_visit_tick->hour = $date_time_data['hours'];
		$db_visit_tick->weekday = $date_time_data['wday'];
		$db_visit_tick->monthday = $date_time_data['mday'];
		$db_visit_tick->month = $date_time_data['mon'];
		
		$db_visit_tick->Save();
		
		// save tics/pages/views
		$new_views_on_page_at_tick = new views_on_page_at_tick();
		$new_views_on_page_at_tick->visit_tickId = $db_visit_tick->visit_tickId;
		foreach($page_views as $page_id => $data) {
			$new_views_on_page_at_tick->wiki_pageId = $pages[$page_id]->wiki_pageId;
			$new_views_on_page_at_tick->views = $data['views'];
			$new_views_on_page_at_tick->SaveNew();
		}
		
		return $db_visit_tick;
	}
	
	function compute_statistics(&$in_pages, $db_visit_tick) {
		
		$stats_to_compute_basic = explode('|', STATS_WHAT_TO_OUTPUT);
		
		$stats_to_compute = array();
		foreach($stats_to_compute_basic as $one_stat_to_compute_basic) {
			$exploded = explode('-', $one_stat_to_compute_basic);
			$item['trigger'] = $exploded[0];
			$item['period_type'] = $exploded[1];
			$item['period_length'] = $exploded[2];
			$item['string'] = $one_stat_to_compute_basic;
			
			$stats_to_compute[] = $item;
		}
		unset($exploded);
		unset($item);
		unset($stats_to_compute_basic);
	
		print_r($stats_to_compute);
	
		foreach($stats_to_compute as $one_stat_to_compute) {
			switch($one_stat_to_compute['trigger']) {
				case 'on_hour':
					$this_hour_ts = $db_visit_tick->hour_timestamp;
					// $other_hour_tick = new visit_tick();
					// $other_hour_tick = $other_hour_tick->GetList(array(array('hour_timestamp', '>=', $other_hour_ts)), 'hour_timestamp', true, 1);
					// if(count($other_hour_tick) != 1) {
						// Log::get_instance()->log(Log::LEVEL_ERROR, '[' . __FILE__ . ':' . __LINE__ .'] other_hour_tick not found; query: '. $other_hour_tick->pog_query);
						// continue;
					// }
					// $other_hour_tick = $other_hour_tick[0];
					
					if($one_stat_to_compute['period_type'] == 'last') {
						$other_hour_ts = $db_visit_tick->hour_timestamp - $one_stat_to_compute['period_length'] * 60 * 60;
						
						// delete old list
						$db_diff_identifier = new diff_identifier();
						$db_diff_identifier->DeleteList(array(array('tag', '=', $one_stat_to_compute['string'])), true);
						
						$db_diff_identifier = new diff_identifier();
						$db_diff_identifier->tag = $one_stat_to_compute['string'];
						$db_diff_identifier->Save();
						
						$db_diff_record = new diff_record();
						$db_diff_record->diff_identifierId = $db_diff_identifier->diff_identifierId;

						$db_views_on_page_at_tick_now = new views_on_page_at_tick();
						$db_views_on_page_at_tick_earlier = new views_on_page_at_tick_suppelemented();
						
						foreach($in_pages as $db_wiki_page){
							// views now
							$db_views_on_page_at_tick_now_list = $db_views_on_page_at_tick_now->GetList(
								array
								(
									array('wiki_pageid', '=', $db_wiki_page->wiki_pageId)
								,	array('visit_tickid', '=', $db_visit_tick->visit_tickId)
								)
								, 'views_on_page_at_tickId'
								, true
								, 1);
							if(!is_array($db_views_on_page_at_tick_now_list) || count($db_views_on_page_at_tick_now_list) != 1) {
								Log::get_instance()->log(Log::LEVEL_ERROR, '[' . __FILE__ . ':' . __LINE__ .'] db_views_on_page_at_tick_now not found; query: '. $db_views_on_page_at_tick_now->pog_query);
								continue;
							}
							$db_views_on_page_at_tick_now = $db_views_on_page_at_tick_now_list[0];
							
							//print_r(get_class_methods($db_views_on_page_at_tick_earlier));
							//die();
							
							// views earlier
							$db_views_on_page_at_tick_earlier_list = $db_views_on_page_at_tick_earlier->GetOldestViewsFromTime($db_wiki_page, 'hour', $other_hour_ts);
								
							if(!is_array($db_views_on_page_at_tick_earlier_list) || count($db_views_on_page_at_tick_earlier_list) != 1) {
								Log::get_instance()->log(Log::LEVEL_ERROR, '[' . __FILE__ . ':' . __LINE__ .'] db_views_on_page_at_tick_earlier not found; query: '. $db_views_on_page_at_tick_earlier->pog_query);
								continue;
							}
							$db_views_on_page_at_tick_earlier = $db_views_on_page_at_tick_earlier_list[0];
							
							if($one_stat_to_compute['period_length'] == 1) {
								if($db_views_on_page_at_tick_now->views_on_page_at_tickId == $db_views_on_page_at_tick_earlier->views_on_page_at_tickId) {
									//print("Same views tick id $other_hour_ts =? ".$db_visit_tick->hour_timestamp."\n");
								}
							}
							
							$db_diff_record->wiki_pageId = $db_wiki_page->wiki_pageId;
							$db_diff_record->views = $db_views_on_page_at_tick_now->views - $db_views_on_page_at_tick_earlier->views;
							//print($db_diff_record->views . " = " . $db_views_on_page_at_tick_now->views . " - " . $db_views_on_page_at_tick_earlier->views . "\n");
							
							$db_diff_record->SaveNew();
						}
					}
					
					break;
			}
		}
	}

	function publish_statistics() {
		$stats_to_compute_basic = explode('|', STATS_WHAT_TO_OUTPUT);

		$data = array();
		$identifiers = array();
		
		// Get all data
		foreach($stats_to_compute_basic as $one_stat_to_compute_basic) {

			$db_identifier = $this->get_tag_identifier($one_stat_to_compute_basic);
			if($db_identifier === null) {
				continue;
			}
			$identifiers[$one_stat_to_compute_basic] = $db_identifier;
		
			$data_for_tag = $this->get_stats_for_tag($db_identifier);
			if(count($data_for_tag) == 0) {
				continue;
			}
			
			foreach($data_for_tag as $title => $views) {
				$data[$title][$db_identifier->diff_identifierId] = $views;
			}
		}
		
		// Build page content
		$identifiers_keys = array_keys($identifiers);
		$identifiers_count = count($identifiers);
		$header_content = "{| class=\"sortable wikitable\" \n|+ '''Page views over a period''' \n|- \n! Article Title \n";
		
		foreach($identifiers as $key => $value) {
			$header_content .= "! $key \n";
		}
		//print_r($data);
		//print_r($identifiers_keys);
		$text_content = '';
		foreach($data as $page_title => $views_array) {
			
			$text_content .= "|- \n|| [[:$page_title]]";
			
			for($i = 0; $i < $identifiers_count; ++$i) {
				if(isset($views_array[$identifiers[$identifiers_keys[$i]]->diff_identifierId])) {
					$text_content .= ' || ' . $views_array[$identifiers[$identifiers_keys[$i]]->diff_identifierId];
				}
				else {
					$text_content .= ' || -';
				}
			}
			
			$text_content .= " \n";
		}
		
		$text_content = $header_content . $text_content . "|}\n";
		//print_r($text_content);
		// return;
		// update stats page
		if($this->_api_object->edit(STATS_STATS_PAGE, $text_content, 'Bot:Updating with latest data', true)) {
			Log::get_instance()->log(Log::LEVEL_INFO, '[' . __FILE__ . ':' . __LINE__ .'] publish_statistics success');
		}
		else {
			Log::get_instance()->log(Log::LEVEL_ERROR, '[' . __FILE__ . ':' . __LINE__ .'] publish_statistics failed');
		}
	}
	
	private function get_tag_identifier($in_tag) {
		$db_idenfitifer = new diff_identifier();
		$db_idenfitifer_list = $db_idenfitifer->GetList(array(array('tag', 'LIKE', $in_tag)));
		if(!is_array($db_idenfitifer_list) || count($db_idenfitifer_list) != 1) {
			Log::get_instance()->log(Log::LEVEL_ERROR, '[' . __FILE__ . ':' . __LINE__ .'] db_idenfitifer not found; query: '. $db_idenfitifer->pog_query);
			return null;
		}
		return $db_idenfitifer_list[0];
	}
	
	private function get_stats_for_tag($in_identifier) {
		
		$connection = Database::Connect();
		$query = 'SELECT `title`, `views` from `views_on_pages_summarized` WHERE `diff_identifierid` = ' . $in_identifier->diff_identifierId;
		
		$cursor = Database::Reader($query, $connection);
		
		$data_array = array();
		while ($row = Database::Read($cursor))
		{
			$data_array[$in_identifier->Unescape($row['title'])] = $in_identifier->Unescape($row['views']);
		}
		
		return $data_array;
	}
 }